package com.bestvike.stone.spring.shiro.aop;

import com.bestvike.stone.spring.shiro.SecurityUtils;
import com.bestvike.stone.spring.shiro.annotation.handler.AuthorizingAnnotationHandler;
import com.bestvike.stone.spring.shiro.annotation.handler.GuestAnnotationHandler;
import com.bestvike.stone.spring.shiro.annotation.handler.PermissionAnnotationHandler;
import com.bestvike.stone.spring.shiro.annotation.handler.RoleAnnotationHandler;
import com.bestvike.stone.spring.shiro.annotation.handler.UserAnnotationHandler;
import com.bestvike.stone.spring.shiro.annotation.resolver.AnnotationResolver;
import com.bestvike.stone.spring.shiro.authz.exception.AuthorizationException;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

/**
 * 注解拦截器
 */
@CommonsLog
public final class AnnotationsAuthorizingMethodInterceptor implements AuthorizingMethodInterceptor {
    private final Collection<AuthorizingAnnotationHandler> handlers;

    /**
     * 构造函数
     */
    public AnnotationsAuthorizingMethodInterceptor() {
        this.handlers = new ArrayList<>(4);
        this.handlers.add(new RoleAnnotationHandler());
        this.handlers.add(new PermissionAnnotationHandler());
        this.handlers.add(new UserAnnotationHandler());
        this.handlers.add(new GuestAnnotationHandler());
    }

    /**
     * 获取注解处理器
     */
    public Collection<AuthorizingAnnotationHandler> getHandlers() {
        return this.handlers;
    }

    /**
     * 方法调用前执行
     */
    @Override
    public Object invoke(JoinPoint joinPoint) throws AuthorizationException {
        if (log.isTraceEnabled()) {
            log.trace("#### Invoking a method decorated with a Shiro annotation" +
                    "\n\tkind       : " + joinPoint.getKind() +
                    "\n\tjoinpoint  : " + joinPoint +
                    "\n\tannotations: " + Arrays.toString(((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotations()) +
                    "\n\ttarget     : " + joinPoint.getTarget());
        }
        final MethodInvocation methodInvocation = BeforeAdviceMethodInvocation.createFrom(joinPoint);
        this.assertAuthorized(methodInvocation);
        return null;
    }

    /**
     * 获取注解解析器
     */
    private AnnotationResolver getResolver() {
        return SecurityUtils.getAnnotationResolver();
    }

    /**
     * 断言权限是否通过
     */
    private void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
        //default implementation just ensures no deny votes are cast:
        final Collection<AuthorizingAnnotationHandler> handlers = this.getHandlers();
        if (handlers == null || handlers.isEmpty())
            return;

        final AnnotationResolver resolver = this.getResolver();
        for (AuthorizingAnnotationHandler handler : handlers) {
            final Annotation annotation = resolver.getAnnotation(methodInvocation, handler.getAnnotationClass());
            if (annotation == null)
                continue;
            handler.assertAuthorized(annotation);
        }
    }
}
